/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.commons;

import java.util.logging.Level;
import org.balisunrise.application.Application;
import org.balisunrise.builder.FactoryException;
import org.balisunrise.common.Criterion;
import org.balisunrise.annotation.FilterType;
import org.balisunrise.common.ConverterException;
import org.balisunrise.common.EntityFieldType;
import org.balisunrise.common.FieldComponent;
import org.balisunrise.common.FieldInput;

/**
 *
 * @author Glauco
 */
public class FilterField extends FieldLayout
        implements org.balisunrise.common.FilterField{

    public static final String PROP_FIELD_PRE_NAME = "fieldPreName";
    public static final String PROP_FILTER_TYPE = "filterType";
    public static final String PROP_EMPTY_FOT_ALL = "emptyForAll";
    public static final String PROP_CRITERION = "criterion";

    private String fieldPreName;
    private FilterType filterType;
    private boolean emptyForAll;
    private Criterion criterion;

    private FieldInput field1;
    private FieldInput field2;
    private Object oldValue1;
    private Object oldValue2;

    public String getFieldPreName() {
        return fieldPreName;
    }

    public void setFieldPreName(String fieldPreName) {
        String old = this.fieldPreName;
        this.fieldPreName = fieldPreName;
        changeSupport.firePropertyChange(PROP_FIELD_PRE_NAME, old, fieldPreName);
    }

    public boolean isEmptyForAll() {
        return emptyForAll;
    }

    public FilterType getFilterType() {
        return filterType;
    }

    public void setCriterion(Criterion criterion) {
        Criterion old = this.criterion;
        this.criterion = criterion;
        changeSupport.firePropertyChange(PROP_CRITERION, old, criterion);
    }

    /**
     * Adiciona um nome predecessor ao nome do field.
     * É utilizado para filtrar atravéz de campos encapsulados por entidades
     * de entidades.
     * Por Exemplo:
     * A Classe Estado tem uma referencia a Pais, então para filtrar os
     * estados pelo nome do pais usariamos "pais.nome" onde "nome" seria o
     * campo e "pais" seria o pré-name.
     *
     * @param prefix
     */
    @Override
    public void addPrefix(String prefix) {
        if (fieldPreName == null || fieldPreName.isEmpty())
            setFieldPreName(prefix);
        else
            setFieldPreName(prefix + "." + fieldPreName);
    }

    /**
     * Atribui o tipo de filtro que será usado pelo Criterion
     *
     * @param filterType
     */
    @Override
    public void setFilterType(FilterType filterType) {
        FilterType old = this.filterType;
        this.filterType = filterType;
        changeSupport.firePropertyChange(PROP_FILTER_TYPE, old, filterType);
    }

    /**
     * Atribui se emptyForAll para o Criterion
     *
     * @param emptyForAll
     */
    @Override
    public void setEmptyForAll(boolean emptyForAll) {
        boolean old = this.emptyForAll;
        this.emptyForAll = emptyForAll;
        changeSupport.firePropertyChange(PROP_EMPTY_FOT_ALL, old, emptyForAll);
    }

    /**
     * Pega da lista de fields os FieldInput para criar os Criterions.
     */
    private void getFieldInputs(){
        boolean first = true;
        for (FieldComponent fc : fields) {
            if(fc instanceof FieldInput){
                if(first)
                    field1 = (FieldInput) fc;
                else
                    field2 = (FieldInput) fc;
            }
        }
    }

    /**
     * Retorna um Criterion.
     */
    @Override
    public Criterion getCriterion() {

        if (criterion == null)
            try{
                Criterion criterionAux = Application.get()
                        .getFactory()
                        .createBaliCriterion(fieldName);
                if(fieldPreName != null && fieldPreName.length() > 0){
                    if(fieldPreName.contains("."))
                        for (String s : fieldPreName.split(".")) {
                            criterionAux.addPreName(s);
                        }
                    else
                        criterionAux.addPreName(fieldPreName);
                }
                criterionAux.setOwner(this);
                setCriterion(criterionAux);
            }catch (FactoryException fex){
                String msg = "FilterField não pode criar Criterion: " 
                        + fex.getMessage();
                Application.get().getLogger().log(Level.SEVERE, msg, fex);
            }

        getFieldInputs();

        oldValue1 = null;
        oldValue2 = null;

        try{
            if(field1 != null){
                oldValue1 = field1.getComponentManager().readConvertedValue(
                        field1.getVisualRepresentation(), emptyForAll);
            }
            if(field2 != null){
                oldValue2 = field2.getComponentManager().readConvertedValue(
                        field2.getVisualRepresentation(), emptyForAll);
            }
        }catch (ConverterException ccex){
            String msg = "FilterField não pode ler os dados dos componentes"
                    + ccex.getMessage();
            Application.get().getLogger().log(Level.SEVERE, msg, ccex);
        }

        criterion.setFilterType(getFilterType());
        criterion.setEmptyForAll(isEmptyForAll());
        criterion.setValue(oldValue1);
        criterion.setOtherValue(oldValue2);

        return criterion;
    }

    /**
     * Verifica se os valores do filtro foram modificados desde a última
     * chamada getCriterion().
     */
    @Override
    public boolean isModified() {

        getFieldInputs();

        Object obj1 = null;
        Object obj2 = null;

        try{
            if(field1 != null){
                obj1 = field1.getComponentManager().readConvertedValue(
                        field1.getVisualRepresentation(), emptyForAll);
            }
            if(field2 != null){
                obj2 = field2.getComponentManager().readConvertedValue(
                        field2.getVisualRepresentation(), emptyForAll);
            }
        }catch (ConverterException ccex){
            String msg = "FilterField não pode ler os dados dos componentes"
                    + ccex.getMessage();
            Application.get().getLogger().log(Level.SEVERE, msg, ccex);
        }

        return (!(oldValue1 == null && obj1 == null) &&
                !(oldValue1 != null && oldValue1.equals(obj1))) ||
                (!(oldValue2 == null && obj2 == null) &&
                !(oldValue2 != null && oldValue2.equals(obj2)));

    }

    @Override
    public EntityFieldType getEntityFieldType() {
        return EntityFieldType.FILTER_FIELD;
    }
}
